#include "Cartridge.h"
#include "Log.h"
#include "Mapper.h"
#include <fstream>
#include <string>

PNE_NAMESPACE_BEGIN

Cartridge::Cartridge() : m_nameTableMirroring(0), m_mapperNumber(0), m_extendedRAM(false) {}

const std::vector<Byte> &Cartridge::GetROM()
{
    return m_PRG_ROM;
}

const std::vector<Byte> &Cartridge::GetVROM()
{
    return m_CHR_ROM;
}

Byte Cartridge::GetMapper()
{
    return m_mapperNumber;
}

Byte Cartridge::GetNameTableMirroring()
{
    return m_nameTableMirroring;
}

bool Cartridge::HasExtendedRAM()
{
    return true;
}
// 解析nes 文件
bool Cartridge::LoadFromFile(std::string path)
{
    std::ifstream romFile(path, std::ios_base::binary | std::ios_base::in);
    // 没有rom文件
    if (!romFile)
    {
        LOG(Level::Error) << "Could not open ROM file from path: " << path << std::endl;
        return false;
    }
    // 开始读取rom文件
    std::vector<Byte> header;
    LOG(Level::Info) << "Reading ROM from path: " << path << std::endl;

    // Header
    header.resize(0x10);
    if (!romFile.read(reinterpret_cast<char *>(&header[0]), 0x10))
    {
        LOG(Level::Error) << "Reading iNES header failed." << std::endl;
        return false;
    }

    if (std::string{&header[0], &header[4]} != "NES\x1A")
    {
        LOG(Level::Error) << "Not a valid iNES image, Magic number: " << std::hex
                          << header[0] << " " << header[1] << " " << header[2] << " " << int(header[3]) << std::endl
                          << "Valid magic number: N E S 1 a" << std::endl; // 检查合法nes image头
        return false;
    }

    // 读取NES 头
    LOG(Level::Info) << "Reading header, it dictates: \n";

    Byte banks = header[4];
    // 有没有PRG ROM banks呀?
    LOG(Level::Info) << "16KB PRG-ROM Banks: " << +banks << std::endl;

    if (!banks)
    {
        LOG(Level::Error) << "ROM has no PRG-ROM banks, Loading ROM failed." << std::endl;
        return false;
    }

    Byte vbanks = header[5];
    // 有没有CHR ROM Banks
    LOG(Level::Info) << "8KB CHR-ROM Banks: " << +vbanks << std::endl;

    // 判断怎么展示
    if (header[6] & 0x8)
    {
        m_nameTableMirroring = static_cast<Byte>(NameTableMirroring::FourScreen);
        LOG(Level::Info) << "Name Table Mirroring: " << "FourScreen" << std::endl;
    }
    else
    {
        m_nameTableMirroring = header[6] & 0x1;
        LOG(Level::Info) << "Name Table Mirroring: " << (m_nameTableMirroring == 0 ? "Horizontal" : "Vertical") << std::endl;
    }

    m_mapperNumber = ((header[6] >> 4) & 0xf) | (header[7] & 0xf0);
    LOG(Level::Info) << "Mapper #: " << +m_mapperNumber << std::endl;

    m_extendedRAM = header[6] & 0x2;
    LOG(Level::Info) << "Extended (CPU) RAM: " << std::boolalpha << m_extendedRAM << std::endl;

    if (header[6] & 0x4)
    {
        LOG(Level::Error) << "Trainer is not supported." << std::endl;
        return false;
    }

    if ((header[0xA] & 0x3) == 0x2 || (header[0xA] & 0x1))
    {
        LOG(Level::Error) << "PAL ROM not supported." << std::endl;
        return false;
    }
    else
    {
        LOG(Level::Info) << "ROM is NTSC compatible" << std::endl;
    }

    // PRG-ROM 16KB banks
    m_PRG_ROM.resize(0x4000 * banks);
    if (!romFile.read(reinterpret_cast<char *>(&m_PRG_ROM[0]), 0x4000 * banks))
    {
        LOG(Level::Error) << "Reading PRG-ROM from image file failed." << std::endl;
        return false;
    }

    // CHR-ROM 8KB banks
    if (vbanks)
    {
        m_CHR_ROM.resize(0x2000 * vbanks);
        if (!romFile.read(
                reinterpret_cast<char *>(&m_CHR_ROM[0]),
                0x2000 * vbanks))
        {
            LOG(Level::Error) << "Reading CHR-ROM from image file failed" << std::endl;
            return false;
        }
    }
    else
    {
        LOG(Level::Info) << "Cartridge with CHR-ROM." << std::endl;
    }
    return true;
}

PNE_NAMESPACE_END
// DONE, CHECK